package com.ruoyi.framework.config

import org.apache.commons.lang3.StringUtils
import org.apache.ibatis.io.VFS
import org.apache.ibatis.session.SqlSessionFactory
import org.mybatis.spring.SqlSessionFactoryBean
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.env.Environment
import org.springframework.core.io.DefaultResourceLoader
import org.springframework.core.io.Resource
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
import org.springframework.core.io.support.ResourcePatternResolver
import org.springframework.core.type.classreading.CachingMetadataReaderFactory
import org.springframework.core.type.classreading.MetadataReader
import org.springframework.core.type.classreading.MetadataReaderFactory
import org.springframework.util.ClassUtils
import java.io.IOException
import java.util.*
import javax.sql.DataSource

/**
 * Mybatis支持*匹配扫描包
 *
 * @author ruoyi
 */
@Configuration
open class MyBatisConfig {
    @Autowired
    private val env: Environment? = null

    companion object {

        private const val DEFAULT_RESOURCE_PATTERN = "**/*.class"

        fun setTypeAliasesPackage(typeAliasesPackage: String): String {
            var newTypeAliasesPackage = typeAliasesPackage
            val resolver = PathMatchingResourcePatternResolver() as ResourcePatternResolver
            val metadataReaderFactory: MetadataReaderFactory = CachingMetadataReaderFactory(resolver)
            val allResult: MutableList<String> = ArrayList()

            try {
                newTypeAliasesPackage.split(",".toRegex()).dropLastWhile { it.isEmpty() }
                    .toTypedArray().forEach { aliasesPackage ->

                        val result: MutableList<String> = ArrayList()

                        val resources = resolver.getResources(
                            ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                                    + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim { it <= ' ' })
                                    + "/"
                                    + DEFAULT_RESOURCE_PATTERN
                        )

                        if (resources.isNotEmpty()) {
                            resources.forEach { resource ->
                                if (resource.isReadable) {
                                    try {
                                        result.add(
                                            Class.forName(
                                                metadataReaderFactory.getMetadataReader(resource).classMetadata.className
                                            ).getPackage().name
                                        )
                                    } catch (e: ClassNotFoundException) {
                                        e.printStackTrace()
                                    }
                                }
                            }
                        }

                        if (result.size > 0) {
                            val hashResult = HashSet(result)
                            allResult.addAll(hashResult)
                        }

                    }
                newTypeAliasesPackage = if (allResult.size > 0) {
                    allResult.joinToString(",")
                } else {
                    throw RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + newTypeAliasesPackage + "未找到任何包")
                }
            } catch (e: IOException) {
                e.printStackTrace()
            }
            return newTypeAliasesPackage
        }

    }

    private fun resolveMapperLocations(mapperLocations: Array<String?>?): Array<Resource> {
        val resourceResolver: ResourcePatternResolver = PathMatchingResourcePatternResolver()
        val resources: MutableList<Resource> = ArrayList()
        mapperLocations?.forEach { mapperLocation ->
            try {
                val mappers = resourceResolver.getResources(mapperLocation!!)
                resources.addAll(listOf(*mappers))
            } catch (e: IOException) {
                // ignore
            }
        }
        return resources.toTypedArray()
    }

    @Bean
    @Throws(Exception::class)
    open fun sqlSessionFactory(dataSource: DataSource?): SqlSessionFactory? {
        var typeAliasesPackage = env!!.getProperty("mybatis.typeAliasesPackage")
        val mapperLocations = env.getProperty("mybatis.mapperLocations")
        val configLocation = env.getProperty("mybatis.configLocation")
        typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage!!)
        VFS.addImplClass(SpringBootVFS::class.java)
        val sessionFactory = SqlSessionFactoryBean()
        sessionFactory.setDataSource(dataSource)
        sessionFactory.setTypeAliasesPackage(typeAliasesPackage)
        sessionFactory.setMapperLocations(*resolveMapperLocations(StringUtils.split(mapperLocations, ",")))
        sessionFactory.setConfigLocation(DefaultResourceLoader().getResource(configLocation!!))
        return sessionFactory.getObject()
    }
}
